﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading;
using System.Net;
using System.Net.Sockets;
using Ling.P2P.Provider;
using Ling.P2P.DemoLib;

namespace Ling.P2P.PServer
{
    public partial class SvrMainWnd : Form
    {
        private ClientInfoCollections LogInClients;
        private TcpP2PServerEntry P2PServer;
        public SvrMainWnd()
        {
            InitializeComponent();

            LogInClients = new ClientInfoCollections();
            dataGridView1.DataSource = LogInClients;

            P2PServer = new TcpP2PServerEntry();
            P2PServer.UseAddressFamily = AddressFamily.InterNetwork; // 指定 P2P 要用的IP地址版本。
            P2PServer.LocalIP = IPAddress.Parse("192.168.1.9");
            P2PServer.LocalPort = 9521; // 指定服务端的端口号。
            P2PServer.ClientLogIn += new LogInStatusEventHandler(P2PServer_ClientLogIn);
            P2PServer.ClientLogOut += new LogInStatusEventHandler(P2PServer_ClientLogOut);
            P2PServer.ClientMaintain += new LogInStatusEventHandler(P2PServer_ClientMaintain);
        }

        protected override void OnLoad(EventArgs e)
        {
            base.OnLoad(e);

            if (!DesignMode)
            {

                IPAddress[] Arry = Dns.GetHostAddresses(Dns.GetHostName());
                foreach (IPAddress IP in Arry)
                    cmb_IP.Items.Add(IP);
            }
        }

        #region P2PServer事件处理

        private void P2PServer_ClientLogIn(TcpP2PEntry sender, Socket skt)
        {
            Invoke(new MethodInvoker(delegate { status_msg.Text = "有客户端正在登入中......"; }));
            // 接收到登入消息后发过来的数据。
            byte[] data = P2PServer.ReceiveKnownData(skt, sizeof(int)); // 先接收数据长度。
            data = P2PServer.ReceiveKnownData(skt, BitConverter.ToInt32(data, 0)); // 然后接收其内容。
            ClientInfo c = ClientInfo.Deserialization(data);
            if (c != null)
            {
                c.IPAddress = ((IPEndPoint)skt.RemoteEndPoint).Address.ToString();
                c.Port = ((IPEndPoint)skt.RemoteEndPoint).Port;
                if (LogInClients.Exists(c))
                {   // 如果名称已经存在回馈登入失败（组建一个消息发回去）。
                    P2PMessage m = new P2PMessage();
                    m.MessageType = P2PConstants.MSG_LOG_FAIL;
                    m.ContentLength = P2PMessage.GetSize();
                    P2PServer.SendMessage(m, skt); // 发送登录失败的消息。
                    // 消息发送后，接着发送为什么失败。
                    P2PServer.SendData(Encoding.UTF8.GetBytes("登录使用的名称已被占用。"), skt);
                }
                else
                {   // 登入成功时组建并回发登入成功的消息
                    P2PMessage m = new P2PMessage();
                    m.MessageType = P2PConstants.MSG_LOG_OK;
                    m.ContentLength = P2PMessage.GetSize();
                    P2PServer.SendMessage(m, skt);
                    Invoke(new MethodInvoker(delegate 
                    {
                        lock (LogInClients)
                            LogInClients.Add(c); 
                    }));
                    // 将已登入的用户列表发给该客户端。
                    data = ClientInfoCollections.Serialization(LogInClients);
                    P2PServer.SendData(data, skt);

                    // 最后将该客户端的信息告诉已登入的客户端。
                    TellClientUpdate(c);
                }
            }
            // 因为 P2PServer 的内部是多线程的，所以最好使用 Invoke 将代码封送回主线程去执行。
            Invoke(new MethodInvoker(delegate 
            {
                status_msg.Text = "服务正在运行中。";
            }));
            // Socket 对象使用完成后销毁它，否则连接的客户端一多众多的 socket 未释放将会占用巨大的资源。
            skt.Close();
            skt.Dispose();
        }

        private void TellClientUpdate(ClientInfo ci)
        {
            lock (LogInClients)
            {
                foreach (ClientInfo c in LogInClients)
                {
                    if (c.Name == ci.Name) continue; // 如果上刚登入的客户端自身就不必通知更新了。
                    Thread thd = new Thread(new ThreadStart(delegate 
                    {
                        byte[] data = ClientInfo.Serialization(ci);
                        P2PMessage m = new P2PMessage();
                        m.MessageType = P2PConstants.MSG_UPDATE_CLIENTS;
                        m.ContentLength = data.Length;
                        IPEndPoint tP = new IPEndPoint(IPAddress.Parse(c.IPAddress), c.Port);
                        Socket skt = P2PServer.SendMessage(m, tP);
                        P2PServer.SendData(data, skt);
                        skt.Close();
                        skt.Dispose();
                    }));
                    thd.Start();
                }
            }
        }

        private void P2PServer_ClientLogOut(TcpP2PEntry sender, Socket skt)
        { 
            //
        }

        private void P2PServer_ClientMaintain(TcpP2PEntry sender, Socket skt)
        {
            //throw new NotImplementedException();
        }

        #endregion

        private void StartP2PServer()
        {
            P2PServer.LocalIP = IPAddress.Parse(cmb_IP.Text.Trim());
            P2PServer.LocalPort = int.Parse(txt_Port.Text.Trim());
            P2PServer.Start();
        }

        private void btn_switch_Click(object sender, EventArgs e)
        {
            if (btn_switch.Text == "启动服务")
            {
                try
                {
                    IPAddress ip = IPAddress.Parse(cmb_IP.Text.Trim());
                }
                catch
                {
                    MessageBox.Show("IP地址无效。", "提示信息");
                    return;
                }
                try
                {
                    int p = int.Parse(txt_Port.Text.Trim());
                }
                catch
                {
                    MessageBox.Show("指定的端口无效。", "提示信息");
                }

                StartP2PServer();
                btn_switch.Text = "停止服务";
                status_msg.Text = "服务正在运行中。";
                cmb_IP.Enabled = false;
                txt_Port.Enabled = false;
            }
            else
            {
                P2PServer.StopListen = true;
                btn_switch.Text = "启动服务";
                status_msg.Text = "服务未启动。";
                cmb_IP.Enabled = true;
                txt_Port.Enabled = true;
            }
        }
    }
}
